package actions;

import img.info.OmeroImage;
import img.info.SampleImage;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import matlab.ResultSeeker;
import matlab.SearchDir;

import omero.ServerError;
import omero.model.FileAnnotation;
import omero.model.Image;
import omero.model.Roi;
import omero.model.RoiI;
import pojos.DatasetData;
import pojos.ImageData;
import pojos.PixelsData;
import cli.OmeroServer;
import cli.FileOperations;
import cli.Logging;


import cli.Settings;
import exception.DataException;
import exception.MatlabException;
import exception.ServerConnectionException;

public class Searching {

	//matlab algorithm used for the search
//	public static final String matlabAlgorithmName = "mat_a1";
	public static final String searchImageLocation = Settings.getSearchImageDir();  // is fixed to './data'
	public static final String searchIndexLocation = Settings.getSearchIndexDir();  // is fixed to './db'
	public static final String searchResultLocation = Settings.getSearchResultDir();		
	
	private static final String queryFrmFullFilename = searchResultLocation + "/" + "query_frm.out";

	private static String roiFullFilename;
	private static int[] region = new int[4];
	
	private static List<ImageData>  omeroImageList = new ArrayList<ImageData>();
	private static DatasetData dataset;
	
	private static long roiId;
	
	public Searching() {	
	}

	/**
	 * Start a search of roiId in the set indentified by datasetId
	 * Ensures that all images in the dataset are indexed 
	 * @throws NoSuchAlgorithmException 
	 * @throws DataException 
	 * @throws MatlabException 
	 * @throws Exception 
	 */
	public static void start( long roiId, long datasetId, String matlabAlgo ) 
			throws ServerError, IOException, NoSuchAlgorithmException, DataException, MatlabException {

		Searching.roiId = roiId;

		boolean valid = true;			

		// ensure roiId and datasetId do exist 
		RoiI roi = OmeroServer.getRoiById(roiId);
		if ( roi == null) {				
			throw new DataException("Invalid roi: " + roiId );
		}
		if ( OmeroServer.getImagesInDataSet(datasetId) == null  ) {
			throw new DataException( "Invalid datasetId: " + datasetId );
		}

		// clear the image list
		omeroImageList.clear();

		String roiFullfilename = saveRoiAsImage( roi );

		// check if all images in the dataset are indexed
		System.out.println( "Checking indexing" );
		Indexing.perform( datasetId ) ;
		System.out.println( "Checking indexing finished" );
		//all images in dataset should be indexed now

		System.out.println( "Cleaning up temp dirs...");
		FileOperations.deleteFilesInDir(new File( searchImageLocation ) );
		FileOperations.deleteFilesInDir(new File( searchIndexLocation ) );
		FileOperations.deleteFilesInDir(new File( searchResultLocation ) );

		//get list of images in the dataset
		Set<ImageData> images = OmeroServer.getImagesInDataSet( datasetId );

		Iterator<ImageData> j = images.iterator();
		ImageData image;
		//for each image in the dataset
		String imageDir = Settings.getSearchImageDir();
		String indexDir = Settings.getSearchIndexDir();
		System.out.println( "Downloading " + images.size() + " images and indexes files " );
		while ( j.hasNext() ) {
			image = j.next();

			omeroImageList.add( image);

			//check if image has already .des and .frm attached
			System.out.println( "id " + image.getId() + " - " + image.getName());
			List<FileAnnotation> indexFiles = OmeroServer.getIndexAttachments( image );


			if ( indexFiles == null ) {
				throw new DataException( "No index attachments found for imageID: " + image.getId() );
			}
			
			//get image pixels and store in image search directory (./data)
			String fullFilename =  imageDir + "/" + image.getName();
			BufferedImage imgBuf = OmeroServer.renderImage(image.getId() );
			FileOperations.saveFileAsTIFF(fullFilename, imgBuf );

			//get des and frm attachments for the image and store them at './db'
			//first index file
			//				System.out.println( "Downloading " + images.size() + " indexes to " +  indexDir );
			FileAnnotation indexFile= indexFiles.get(0);
			String fullIndexname = indexDir +  "/" + indexFile.getFile().getName().getValue();
			//				System.out.println( "Downloading index: " + fullIndexname );
			OmeroServer.saveAttachment(fullIndexname, indexFile );
			//second index file
			indexFile= indexFiles.get(1);
			fullIndexname = Settings.getSearchIndexDir() +  "/" + indexFile.getFile().getName().getValue();

			OmeroServer.saveAttachment(fullIndexname, indexFile );
			System.out.println( "\tDownloaded index files" );
		}	


		//start the matlab search
		//images must be in ./data
		//index files (des and frm) must be in ./db
		//resultDir 
		String resultDir = searchResultLocation + "/";
		SearchDir.start( matlabAlgo, roiFullFilename, region,  resultDir  );

		// get the results and print
		System.out.println("Results: " );

		dataset = createDataset();
		//save stuff like roi etc to the dataset
		saveDatasetResults( dataset );
		//Save image results back to Omero
		List<OmeroImage> results = getResults();
		saveImageResults( dataset, results );
	}

	private static String saveRoiAsImage( Roi roi) throws ServerError, IOException   {
		//get roiPixels and store in temp directory		
	
		String fullFilename = Settings.getTempDir() + "/" + "ROI" + roi.getId().getValue() + ".tif" ;
		long id = roi.getId().getValue();
		BufferedImage roiImgBuf = OmeroServer.createImgFromRoi( id );
		region[0] = 0;
		region[1] = 0;
		region[2] = roiImgBuf.getWidth();
		region[3] = roiImgBuf.getHeight();
		roiFullFilename = fullFilename;
		FileOperations.saveFileAsTIFF(fullFilename, roiImgBuf );
		System.out.println( "Saved roi image to " + fullFilename );
		return fullFilename;
	}
	
	public static List<ImageData> getOmeroImageList() {
		return omeroImageList;
	}
	
	/**
	 * Get matlab results and create list of omero images<Br/>
	 * Ranking is attached to omero image: <br/>
	 * - ranking score (double value)
	 * - file name: sorted frame list
	 * - file name: sorted match list
	 * @return List<OmeroImage>
	 */
	private static List<OmeroImage> getResults() {
		List<OmeroImage> results =  ResultSeeker.getResultOmeroImages( omeroImageList);
		for ( OmeroImage result : results ) {	
			System.out.format( "%1$20s - %2$10s ome-image-id: %3$3s\n", result.imageName, result.rank_factor, result.getOmeroImageId() );
			Logging.log( "results", "Image name: " + result.imageName + ", Ranking score: "  + result.rank_factor );
		}
		return results;
	}
	/**
	 * Save the search results back to the Omero database 
	 * 
	 * @param results  List of matching images
	 * @throws ServerError
	 * @throws IOException
	 * @throws NoSuchAlgorithmException 
	 */
	private static void saveImageResults( DatasetData dataset, List<OmeroImage> results ) 
			throws ServerError, IOException, NoSuchAlgorithmException {
		
		String namespace = createNamespace( dataset.getId() );
		// add all the images to the dataset
		Iterator<OmeroImage> i = results.iterator();
		long imageId;
		OmeroImage oImg;
		String fileName;
		String sha1;
		ImageData image;
		while ( i.hasNext() ) {
			oImg = i.next();
			imageId = oImg.getOmeroImageId();
			OmeroServer.addImageToDataset( dataset.getId(),  imageId );		
			// store the ranking value as double annotation to the omero image
			OmeroServer.setImageRankingValue(imageId, oImg.getRank_factor(), namespace  );
			//add the file 'sorted frame list' to the image in the omero repository
			fileName = oImg.getSortedFrmList();
			sha1 = FileOperations.generateSha1( fileName );
			image = OmeroServer.getImage( oImg.getOmeroImageId() );
			OmeroServer.addFileToImage( fileName, "sorted_frm_list.out", sha1, image, namespace);
			//add the file 'sorted match list' to the image in the omero repository
			fileName = oImg.getSortedMatchList();
			sha1 = FileOperations.generateSha1( fileName );
			image = OmeroServer.getImage( oImg.getOmeroImageId() );			
			OmeroServer.addFileToImage( fileName, "sorted_match_list.out", sha1, image, namespace);	
		}
		System.out.println( "" + results.size() + " Result images added to dataset: '" + dataset.getName() + "', id: " + dataset.getId() );

		//Testing purpose only
		Iterator<OmeroImage> j = results.iterator();
		long id;
		OmeroImage omeroImg;
		while ( j.hasNext() ) {
			omeroImg = j.next();
			id = omeroImg.getOmeroImageId();
			double val = OmeroServer.getImageRankingValue(id, namespace  );
			System.out.println("imageid: "  + id + ", namespace: " + namespace + ", value: " + val);
		}
		
	}
	
	/**
	 * Create a new dataset
	 * @return DatasetData
	 * @throws ServerError 
	 */
	private static DatasetData createDataset( ) throws ServerError {
		//get the projectId
		long projectId = OmeroServer.getProjectId();
		//create new dataset
		return OmeroServer.createDataset("results", projectId );		
	}
	
	/** Save some results of search to the dataset containing the result images
	 * 
	 * @throws IOException 
	 * @throws ServerError 
	 * @throws NoSuchAlgorithmException 
	 */
	private static DatasetData saveDatasetResults( DatasetData dataset ) throws ServerError, IOException, NoSuchAlgorithmException {
		// add the roi keypoint file to the dataset 
		//String fileName = queryFrmFullFilename;
		//String sha1 = FileOperations.generateSha1( fileName );
		// attach the file and the roi id to the dataset
//		OmeroServer.addFileAndNumberToDataset( fileName, null, sha1, roiId, dataset );
		
		String fileName1 = queryFrmFullFilename;
		String sha1 = FileOperations.generateSha1( fileName1 );
//		OmeroServer.addFileToDataset(fileName1, null, sha1, dataset );		
		String fileName2 = roiFullFilename;
		String sha2 = FileOperations.generateSha1( fileName2 );
//		OmeroServer.addFileToDataset(fileName2, null, sha2, dataset );
		
		OmeroServer.addTwoFilesToDataset( fileName1, null, sha1, fileName2, "roi.tif", sha2, dataset );
		
		
		return dataset;
	}
	
	/**
	 * Generate namespace based on the datasetid: Result example 'dataset251'
	 * @param datasetId
	 * @return String returns 'dataset' + number
	 */
	private static String createNamespace( Long datasetId ) {
		return "dataset" + datasetId;
	}

}
